<?php

namespace SunAdmin\Service\Ws;

use think\facade\Cache;
use think\facade\Log;
use think\Exception;

class FdUidBinding extends BaseWebSocketService
{
    /**
     * 绑定fd与uid
     */
    public function bind(string $fd, int $uid): bool
    {
        try {
            $fdKey = $this->getFdKey($fd);
            $uidKey = $this->getUidKey($uid);
            
            // 设置fd->uid映射（永久保存，依赖连接断开事件清理）
            Cache::set($fdKey, $uid);
            
            // 获取uid现有的fd列表
            $fds = Cache::get($uidKey, []);
            if (!in_array($fd, $fds)) {
                $fds[] = $fd;
                Cache::set($uidKey, $fds);
            }
            
            Log::debug("[webSocket] fd-uid绑定成功 | " . json_encode([
                'type' => $this->connectionType,
                'fd' => $fd,
                'uid' => $uid
            ]));
            
            return true;
            
        } catch (\Throwable $e) {
            Log::error("[webSocket] fd-uid绑定失败 | " . json_encode([
                'type' => $this->connectionType,
                'fd' => $fd,
                'uid' => $uid,
                'error' => $e->getMessage()
            ]));
            return false;
        }
    }

    /**
     * 解绑fd
     */
    public function unbind(string $fd): bool
    {
        try {
            $uid = $this->fdToUid($fd);
            if (!$uid) {
                return true; // fd不存在，视为成功
            }
            
            $fdKey = $this->getFdKey($fd);
            $uidKey = $this->getUidKey($uid);
            
            // 删除fd->uid映射
            Cache::delete($fdKey);
            
            // 从uid的fd列表中移除
            $fds = Cache::get($uidKey, []);
            $fds = array_values(array_filter($fds, fn($f) => $f != $fd));
            
            if (empty($fds)) {
                Cache::delete($uidKey);
            } else {
                Cache::set($uidKey, $fds);
            }
            
            Log::debug("[webSocket] fd解绑成功 | " . json_encode([
                'type' => $this->connectionType,
                'fd' => $fd,
                'uid' => $uid
            ]));
            
            return true;
            
        } catch (\Throwable $e) {
            Log::error("[webSocket] fd解绑失败 | " . json_encode([
                'type' => $this->connectionType,
                'fd' => $fd,
                'error' => $e->getMessage()
            ]));
            return false;
        }
    }

    /**
     * 解绑uid的所有连接
     */
    public function unbindByUid(int $uid): bool
    {
        try {
            $fds = $this->uidToFds($uid);
            
            foreach ($fds as $fd) {
                $fdKey = $this->getFdKey($fd);
                Cache::delete($fdKey);
            }
            
            $uidKey = $this->getUidKey($uid);
            Cache::delete($uidKey);
            
            Log::debug("[webSocket] uid解绑成功 | " . json_encode([
                'type' => $this->connectionType,
                'uid' => $uid,
                'fds_count' => count($fds)
            ]));
            
            return true;
            
        } catch (\Throwable $e) {
            Log::error("[webSocket] uid解绑失败 | " . json_encode([
                'type' => $this->connectionType,
                'uid' => $uid,
                'error' => $e->getMessage()
            ]));
            return false;
        }
    }

    /**
     * 通过fd查询uid
     */
    public function fdToUid(string $fd): ?int
    {
        try {
            $fdKey = $this->getFdKey($fd);
            return Cache::get($fdKey);
        } catch (\Throwable $e) {
            Log::error("[webSocket] fd查询uid失败 | " . json_encode([
                'type' => $this->connectionType,
                'fd' => $fd,
                'error' => $e->getMessage()
            ]));
            return null;
        }
    }

    /**
     * 通过uid查询所有fd
     */
    public function uidToFds(int $uid): array
    {
        try {
            $uidKey = $this->getUidKey($uid);
            $fds = Cache::get($uidKey, []);
            // fd现在是字符串格式，不再过滤数字类型
            return is_array($fds) ? array_filter($fds, fn($f) => is_string($f) && strlen($f) > 0) : [];
        } catch (\Throwable $e) {
            Log::error("[webSocket] uid查询fds失败 | " . json_encode([
                'type' => $this->connectionType,
                'uid' => $uid,
                'error' => $e->getMessage()
            ]));
            return [];
        }
    }

    /**
     * 检查fd是否存在
     */
    public function fdExists(string $fd): bool
    {
        try {
            $fdKey = $this->getFdKey($fd);
            return Cache::has($fdKey);
        } catch (\Throwable $e) {
            Log::error("[webSocket] 检查fd存在性失败 | " . json_encode([
                'type' => $this->connectionType,
                'fd' => $fd,
                'error' => $e->getMessage()
            ]));
            return false;
        }
    }

    /**
     * 检查uid是否在线
     */
    public function uidIsOnline(int $uid): bool
    {
        try {
            $fds = $this->uidToFds($uid);
            return !empty($fds);
        } catch (\Throwable $e) {
            Log::error("[webSocket] 检查uid在线状态失败 | " . json_encode([
                'type' => $this->connectionType,
                'uid' => $uid,
                'error' => $e->getMessage()
            ]));
            return false;
        }
    }

    /**
     * 获取所有在线用户ID
     */
    public function getAllOnlineUids(): array
    {
        try {
            $pattern = $this->cachePrefix . '_uid_*';
            $keys = $this->getRedisKeys($pattern);
            
            $onlineUids = [];
            foreach ($keys as $key) {
                if (preg_match('/uid_(\d+)$/', $key, $matches)) {
                    $uid = (int)$matches[1];
                    $fds = Cache::get($key, []);
                    if (!empty($fds)) {
                        $onlineUids[] = $uid;
                    }
                }
            }
            
            return $onlineUids;
            
        } catch (\Throwable $e) {
            Log::error("[webSocket] 获取在线用户列表失败 | " . json_encode([
                'type' => $this->connectionType,
                'error' => $e->getMessage()
            ]));
            return [];
        }
    }

    /**
     * 清除所有连接类型的绑定数据（静态方法，用于系统启动时清理）
     */
    public static function clearAllBindings(): bool
    {
        try {
            $cleanedTotal = 0;
            $connectionTypes = BaseWebSocketService::getAllowedConnectionTypes();
            
            foreach ($connectionTypes as $connectionType) {
                $cachePrefix = $connectionType . '_ws';
                
                // 通过键名模式匹配删除每种连接类型的相关缓存
                $fdPattern = $cachePrefix . '_fd_*';
                $uidPattern = $cachePrefix . '_uid_*';
                
                $redis = Cache::store()->handler();
                if (!$redis instanceof \Redis && !$redis instanceof \Predis\Client) {
                    throw new \Exception("缓存驱动不是Redis实例");
                }
                
                // 获取匹配的键
                $fdKeys = $redis instanceof \Redis ? $redis->keys($fdPattern) : $redis->keys($fdPattern);
                $uidKeys = $redis instanceof \Redis ? $redis->keys($uidPattern) : $redis->keys($uidPattern);
                
                $typeCleanedCount = 0;
                foreach (array_merge($fdKeys, $uidKeys) as $key) {
                    if (Cache::delete($key)) {
                        $typeCleanedCount++;
                    }
                }
                
                $cleanedTotal += $typeCleanedCount;
                
                if ($typeCleanedCount > 0) {
                    Log::info("[webSocket] 清除WebSocket绑定数据 | " . json_encode([
                        'connection_type' => $connectionType,
                        'deleted_keys' => $typeCleanedCount
                    ]));
                }
            }
            
            Log::info("[webSocket] 系统启动时清除所有WebSocket绑定数据完成 | " . json_encode([
                'total_deleted_keys' => $cleanedTotal,
                'connection_types' => $connectionTypes
            ], JSON_UNESCAPED_UNICODE));
            
            return true;
            
        } catch (\Throwable $e) {
            Log::error("[webSocket] 清除所有WebSocket绑定数据失败 | " . json_encode([
                'error' => $e->getMessage()
            ], JSON_UNESCAPED_UNICODE));
            return false;
        }
    }

    /**
     * 获取fd缓存key
     */
    private function getFdKey(string $fd): string
    {
        return "{$this->cachePrefix}_fd_{$fd}";
    }

    /**
     * 获取uid缓存key
     */
    private function getUidKey(int $uid): string
    {
        return "{$this->cachePrefix}_uid_{$uid}";
    }
} 